package com.eugenes.concurrency.runnables;

import java.util.List;

public class Merge< T extends Comparable<T> > implements Runnable {
    
    private final List<T> data;
    private final int offset;
    private final int sizeOne;
    private final int sizeTwo;
    
    private final List<T> cache;
    private final int cacheOffset;
    
    public Merge(List<T> data, int offset, int sizeOne, int sizeTwo,
            List<T> cache, int cacheOffset) {
        
        this.data = data;
        this.offset = offset;
        this.sizeOne = sizeOne;
        this.sizeTwo = sizeTwo;
        
        this.cache = cache;
        this.cacheOffset = cacheOffset;
    }
    
    private T getOneIth(int index) {
        assert (index < sizeOne) : "Index out of first part";
        
        return data.get(offset + index);
    }
    
    private T getTwoIth(int index) {
        assert (index < sizeTwo) : "Index out of second part";
        
        return data.get(offset + sizeOne + index);
    }
    
    private void checkOutOfCache(int index) {
        assert (cacheOffset + index < cache.size()) : "Out of cache";
    }
    
    private T getCache(int index) {
        checkOutOfCache(index);
        return cache.get(cacheOffset + index);
    }
    
    private void setCache(int index, T element) {
        checkOutOfCache(index);
        cache.set(cacheOffset + index, element);
    }
    
    @Override
    public void run() {
        int index1 = 0;
        int index2 = 0;
        int cacheIndex = 0;
        
        while (index1 != sizeOne || index2 != sizeTwo) {
            if (index1 == sizeOne ||
                    (index2 != sizeTwo && getOneIth(index1).compareTo(getTwoIth(index2)) >= 0)) {
                setCache(cacheIndex, getTwoIth(index2));
                ++index2;
            }
            else {
                setCache(cacheIndex, getOneIth(index1));
                ++index1;
            }
            ++cacheIndex;
        }
        
        for (int i=0; i<sizeOne + sizeTwo; ++i) {
            data.set(offset + i, getCache(i));
        }
    }
}
